package com.gzhryc.weixin.gzh;

import com.gzhryc.common.logging.Logger;
import com.gzhryc.common.redis.RedisTools;
import com.gzhryc.common.redis.RedisToolsManager;
import com.gzhryc.weixin.api.DefaultApiResponse;
import com.gzhryc.weixin.api.resource.WxChatApi;
import com.gzhryc.weixin.api.resource.WxResourceApi;
import com.gzhryc.weixin.gzh.enums.ETokenSharedType;
import com.gzhryc.weixin.gzh.models.AccessToken;
import com.gzhryc.weixin.gzh.msg.AccessTokenResponse;
import org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class WeiXinGZHAPIServer {

	static Logger log = Logger.getLogger(WeiXinGZHAPIServer.class);

	private AccessToken accessToken;

	private Integer shareTokenType;
	private String redisKey;
	private Integer redisDB;

	private Timer timer;

	private WxAuthorityApi authorityApi;
	private WxUserApi userApi;
	private WxResourceApi resourceApi;
	private WxChatApi chatApi;
	private WxMenuApi menuApi;
	private WxJSSDKApi jssdkApi;
	private WxBarcodeApi barcodeApi;

	public WeiXinGZHAPIServer(AccessToken accessToken) {
		this.accessToken = accessToken;

		this.shareTokenType = 0;

		this.timer = new Timer();

		this.authorityApi = new WxAuthorityApi(accessToken);
		this.userApi = new WxUserApi(accessToken);
		this.resourceApi = new WxResourceApi(accessToken);
		this.chatApi = new WxChatApi(accessToken);
		this.jssdkApi = new WxJSSDKApi(accessToken);
		this.menuApi = new WxMenuApi(accessToken);
		this.barcodeApi = new WxBarcodeApi(accessToken);
	}
	
	public WeiXinGZHAPIServer(String appid, String secret) {
		this.accessToken = new AccessToken(appid, secret);

		this.shareTokenType = 0;

		this.timer = new Timer();

		this.authorityApi = new WxAuthorityApi(accessToken);
		this.userApi = new WxUserApi(accessToken);
		this.resourceApi = new WxResourceApi(accessToken);
		this.chatApi = new WxChatApi(accessToken);
		this.jssdkApi = new WxJSSDKApi(accessToken);
		this.menuApi = new WxMenuApi(accessToken);
		this.barcodeApi = new WxBarcodeApi(accessToken);
	}

	public WeiXinGZHAPIServer(String appid, String secret, Integer shareTokenType, String redisKey, Integer redisDB) {
		this.accessToken = new AccessToken(appid, secret);

		this.shareTokenType = shareTokenType;
		this.redisKey = redisKey;
		this.redisDB = redisDB;

		this.timer = new Timer();

		this.authorityApi = new WxAuthorityApi(accessToken);
		this.userApi = new WxUserApi(accessToken);
		this.resourceApi = new WxResourceApi(accessToken);
		this.chatApi = new WxChatApi(accessToken);
		this.jssdkApi = new WxJSSDKApi(accessToken);
		this.menuApi = new WxMenuApi(accessToken);
		this.barcodeApi = new WxBarcodeApi(accessToken);
	}

	public void run() {
		accessToken.isRunning(true);
		// 第一次启动获取token
		if (ETokenSharedType.ONLY_GET.index().equals(shareTokenType)) {
			RedisTools redisTools = RedisToolsManager.self().getRedisTools(redisKey);
			log.info("从Redis中获取微信公众号AccessToken，RedisKey：{0},库号：{1}",redisKey,redisDB);
			String accessTokenStr = redisTools.get(redisDB, accessToken.getAppid());
			if (StringUtils.isNotBlank(accessTokenStr)) {
				String[] temp = accessTokenStr.trim().split(" ");
				if (temp != null && temp.length == 3) {
					this.accessToken.setAccess_token(temp[0]);
					this.accessToken.setLastSetDate(new Date(Long.valueOf(temp[1])));
					this.accessToken.setExpires_in(Long.valueOf(temp[2]));
				} else {
					this.accessToken.setAccess_token(accessTokenStr);
					this.accessToken.setLastSetDate(new Date());
					this.accessToken.setExpires_in(7200L);
				}
			}else {
				log.error("{{0}}微信服务从{{1}}获取AccessToken失败",accessToken.getAppid(),redisKey + ":" +redisDB);
			}
		} else {
			AccessTokenResponse accessTokenResponse = authorityApi.getAccessTokenResponse();
			if (accessTokenResponse != null) {
				log.info("{{0}}微信公众号获取AccessToken，信息：{{1:json}}",accessToken.getAppid(),accessTokenResponse);
				accessToken.setAccess_token(accessTokenResponse.getAccess_token());
				accessToken.setLastSetDate(new Date());
				accessToken.setExpires_in(accessTokenResponse.getExpires_in());
				GetTokenTimerTask.start(this);

				if (ETokenSharedType.CREATE_AND_SHARE.index().equals(shareTokenType)) {
					log.info("{{0}}微信公众号设置Redis缓存，RedisKey={{1}}",accessToken.getAppid(),redisKey);
					String accessTokenStr = accessToken.getAccess_token() + " " + accessToken.getLastSetDate().getTime() + " " + accessToken.getExpires_in();
					RedisTools redisTools = RedisToolsManager.self().getRedisTools(redisKey);
					redisTools.set(redisDB, accessToken.getAppid(), accessTokenStr, 7000L);
					log.info("{{0}}微信公众号设置Redis缓存成功，RedisKey={{1}}",accessToken.getAppid(),redisKey);
				}
			}else{
				log.error("{{0}}微信服务请求AccessToken失败，无返回",accessToken.getAppid());
			}
		}
		new Thread() {
			public void run() {
				while (accessToken.isRunning()) {
					try {
						DefaultApiResponse entity = accessToken.queueTake();
						log.error(accessToken.getAppid() + "处理微信API错误结果，错误码：" + entity.getErrcode());
						if (40001 == entity.getErrcode() || 40014 == entity.getErrcode() || 42001 == entity.getErrcode()
								|| 41001 == entity.getErrcode()) {
							if (ETokenSharedType.ONLY_GET.index().equals(shareTokenType)) {
								RedisTools redisTools = RedisToolsManager.self().getRedisTools(redisKey);
								String accessTokenStr = redisTools.get(redisDB, accessToken.getAppid());
								if (StringUtils.isNotBlank(accessTokenStr)) {
									String[] temp = accessTokenStr.split(" ");
									if (temp != null && temp.length == 3) {
										accessToken.setAccess_token(temp[0]);
										accessToken.setLastSetDate(new Date(Long.valueOf(temp[1])));
										accessToken.setExpires_in(Long.valueOf(temp[2]));
									} else {
										accessToken.setAccess_token(accessTokenStr);
										accessToken.setLastSetDate(new Date());
										accessToken.setExpires_in(7200L);
									}
								}else {
									log.error("{{0}}微信服务从{{1}}获取AccessToken失败",accessToken.getAppid(),redisKey +":"+redisDB);
								}
							} else {
								// 检查错误时间与最新的入口凭证获取时间，避免重复获取
								if (entity.getCreateDate() != null && entity.getCreateDate().after(accessToken.getLastSetDate())) {
									AccessTokenResponse accessTokenResponse = authorityApi.getAccessTokenResponse();

									if (accessTokenResponse == null) {
										log.error("获取微信普通AccessToken失败！！！appId="+accessToken.getAppid());
									} else {
										accessToken.setAccess_token(accessTokenResponse.getAccess_token());
										accessToken.setLastSetDate(new Date());
										accessToken.setExpires_in(accessTokenResponse.getExpires_in());

										if (ETokenSharedType.CREATE_AND_SHARE.index().equals(shareTokenType)) {
											String accessTokenStr = accessToken.getAccess_token() + " " + accessToken.getLastSetDate().getTime() + " "
													+ accessToken.getExpires_in();
											RedisTools redisTools = RedisToolsManager.self().getRedisTools(redisKey);
											redisTools.set(redisDB, accessToken.getAppid(), accessTokenStr, 7000L);
										}
									}
								}else{
									log.error("{{0}}微信服务获取AccessToken失败",accessToken.getAppid());
								}
							}
						}
					} catch (Exception e) {
						log.error(accessToken.getAppid() + "微信API响应处理失败", e);
					}
				}
			}

		}.start();
	}

	public void stop() {
		this.accessToken.isRunning(false);
	}

	public boolean isRunning() {
		return this.accessToken.isRunning();
	}

	/**
	 * 获取微信权限服务
	 * 
	 * @return
	 */
	public WxAuthorityApi getAuthorityApi() {
		return authorityApi;
	}

	/**
	 * 获取用户相关API
	 * 
	 * @return
	 */
	public WxUserApi getUserApi() {
		return userApi;
	}

	/**
	 * 获取微信资源服务
	 * 
	 * @return
	 */
	public WxResourceApi getResourceApi() {
		return resourceApi;
	}

	/**
	 * 获取用户相关API
	 * 
	 * @return
	 */
	public WxChatApi getChatApi() {
		return chatApi;
	}

	/**
	 * 获取微信JSSDK服务
	 * 
	 * @return
	 */
	public WxJSSDKApi getJSSDKApi() {
		return jssdkApi;
	}

	/**
	 * 获取微信自定义菜单服务
	 * 
	 * @return
	 */
	public WxMenuApi getMenuApi() {
		return menuApi;
	}

	/**
	 * 获取微信二维码服务
	 * 
	 * @return
	 */
	public WxBarcodeApi getBarcodeApi() {
		return barcodeApi;
	}

	public AccessToken getAccessToken() {
		return this.accessToken;
	}

	public Integer getShareTokenType(){
		return this.shareTokenType;
	}

	public Integer getRedisDB(){
		return this.redisDB;
	}

	public String getRedisKey(){
		return this.redisKey;
	}

	public static class GetTokenTimerTask extends TimerTask {

		final WeiXinGZHAPIServer apiSer;

		public GetTokenTimerTask(WeiXinGZHAPIServer apiSer) {
			this.apiSer = apiSer;
		}

		@Override
		public void run() {
			log.info("～～～定时检查微信公众号服务～～～");
			try {
				if (apiSer != null && apiSer.getAccessToken() != null && apiSer.getAccessToken().getExpires_in() != null) {
					// 超过五分之四的时间就重新刷新
					Long flag = apiSer.getAccessToken().getExpires_in() - (apiSer.getAccessToken().getExpires_in() / 5);
					Long temp = (System.currentTimeMillis() - apiSer.getAccessToken().getLastSetDate().getTime()) / 1000;
					if (temp > flag) {
						AccessTokenResponse accessTokenResponse = apiSer.getAuthorityApi().getAccessTokenResponse();

						if (accessTokenResponse == null) {
							log.error("获取微信普通AccessToken失败！！！"+apiSer.accessToken.getAppid());
						} else {
							apiSer.accessToken.setAccess_token(accessTokenResponse.getAccess_token());
							apiSer.accessToken.setLastSetDate(new Date());
							apiSer.accessToken.setExpires_in(accessTokenResponse.getExpires_in());

							if (ETokenSharedType.CREATE_AND_SHARE.index().equals(apiSer.shareTokenType)) {
								String accessTokenStr = apiSer.accessToken.getAccess_token() + " " + apiSer.accessToken.getLastSetDate().getTime() + " "
										+ apiSer.accessToken.getExpires_in();
								RedisTools redisTools = RedisToolsManager.self().getRedisTools(apiSer.redisKey);
								redisTools.set(apiSer.redisDB, apiSer.accessToken.getAppid(), accessTokenStr, 7000L);
							}
						}
					}
				} else {
					// 如果为空则直接刷新
					AccessTokenResponse accessTokenResponse = apiSer.getAuthorityApi().getAccessTokenResponse();

					if (accessTokenResponse == null) {
						log.error("获取微信普通AccessToken失败！！！"+apiSer.accessToken.getAppid());
					} else {
						apiSer.accessToken.setAccess_token(accessTokenResponse.getAccess_token());
						apiSer.accessToken.setLastSetDate(new Date());
						apiSer.accessToken.setExpires_in(accessTokenResponse.getExpires_in());

						if (ETokenSharedType.CREATE_AND_SHARE.index().equals(apiSer.shareTokenType)) {
							String accessTokenStr = apiSer.accessToken.getAccess_token() + " " + apiSer.accessToken.getLastSetDate().getTime() + " "
									+ apiSer.accessToken.getExpires_in();
							RedisTools redisTools = RedisToolsManager.self().getRedisTools(apiSer.redisKey);
							redisTools.set(apiSer.redisDB, apiSer.accessToken.getAppid(), accessTokenStr, 7000L);
						}
					}
				}
			} catch (Exception e) {
				log.error("获取微信普通AccessToken失败"+apiSer.accessToken.getAppid(), e);
			} finally {
				GetTokenTimerTask.start(apiSer);
			}
		}

		public static void start(WeiXinGZHAPIServer apiSer) {
			// 定时刷新
			if (apiSer.accessToken.isRunning()) {
				Long delay = 30000L; // 默认5分钟
				if (apiSer.getAccessToken() != null && apiSer.getAccessToken().getExpires_in() != null) {
					delay = apiSer.getAccessToken().getExpires_in() - (apiSer.getAccessToken().getExpires_in() / 6);
					delay = delay * 1000;
				}
				apiSer.timer.schedule(new GetTokenTimerTask(apiSer), delay);
			}
		}
	}
}
